package com.ruoyi.common.mybatisplus;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
//import com.google.common.collect.Lists;
//import com.google.common.collect.Maps;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ruoyi.common.exception.RRException;
import com.ruoyi.common.jdkext.Function2;
import com.ruoyi.common.utils.SqlUtils;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
//import org.apache.commons.lang.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Data
public class WhereCondition<T> {

    private Page<T> page = new Page<>();

    private HashMap<String, Object> where =  Maps.newHashMap();

    private T queryObject;

    //数据库表别名
    private String aliasName;

    public <R> Page<R> translatePage(){
        Page<T> sPage=getPage();
        if(sPage==null){
            return null;
        }
        Page<R> nPage=new Page<>();
        nPage.setTotal(sPage.getTotal());
        nPage.setSize(sPage.getSize());
        nPage.setCurrent(sPage.getCurrent());
//		nPage.setOptimizeCountSql(sPage.set)
        nPage.setOrders(sPage.getOrders());
//		nPage.setSearchCount(sPage.gets)
        nPage.setPages(sPage.getPages());
        return nPage;
    }

    public static String SYM_EQUAL;
    public static String SYM_NOT_EQUAL;
    public static String SYM_LESS_THAN;
    public static String SYM_LESS_OR_EQUAL_THAN;
    public static String SYM_GREATER_THAN;
    public static String SYM_GREATER_OR_EQUALTHAN;
    public static String SYM_IN;
    public static String SYM_NOT_IN;
    public static String SYM_LIKE;
    public static String SYM_NOT_LIKE;
    public static String SYM_LEFT_LIKE;
    public static String SYM_RIGHT_LIKE;
    public static String SYM_IS_NULL;
    public static String SYM_IS_NOT_NULL;
    public static String SYM_GROUP_BY;
    public static String SYM_HAVING;
    public static String SYM_ORDER_BY_ASCENDING;
    public static String SYM_ORDER_BY_DESCENDING;

    //#region 暂未使用的查询条件
//	public static String SYM_LIKE_MORE;
//	public static String SYM_NOT_LIKE_MORE;
//	public static String SYM_LEFT_LIKE_MORE;
//	public static String SYM_NOT_LEFT_LIKE_MORE;
//	public static String SYM_RIGHT_LIKE_MORE;
//	public static String SYM_NOT_RIGHT_LIKE_MORE;
    //#endregion

    public static Map<String, String> DATA_PERMISSION_MASTER_ACCOUNTS;

    @Getter
    private Map<String, Object> unwrappedArgs = Maps.newHashMap();
    @Getter private QueryWrapper<T> _queryWrapper;

    private boolean isAccountPermitted(String account) {
        return DATA_PERMISSION_MASTER_ACCOUNTS.containsKey(account);
    }

    @SuppressWarnings("unchecked")
    private static AbstractMap.SimpleEntry<Pattern, SimpleDateFormat>[] datePatterns=new AbstractMap.SimpleEntry[]{
        new AbstractMap.SimpleEntry(Pattern.compile("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")),
        new AbstractMap.SimpleEntry(Pattern.compile("\\d{4}-\\d{2}-\\d{2}"), new SimpleDateFormat("yyyy-MM-dd")),
        new AbstractMap.SimpleEntry(Pattern.compile("\\d{2}:\\d{2}:\\d{2}"), new SimpleDateFormat("HH:mm:ss")),
        new AbstractMap.SimpleEntry(Pattern.compile("\\d{4}-\\d{2}"), new SimpleDateFormat("yyyy-MM")),
        new AbstractMap.SimpleEntry(Pattern.compile("\\d{2}:\\d{2}"), new SimpleDateFormat("mm:ss"))
    };

    private String camelToUnderline(String fullName, String pattern) {
        String result = fullName.replaceFirst(pattern, "");
        // 将驼峰命名还原为数据库字段名
        int index = result.indexOf(".");
        if (index > 0) {
            String aliasName = result.substring(0, index);
            String fieldName = com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(result.substring(index + 1));
            return aliasName + "." + fieldName;
        }
        return com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(result);
    }

    private Object escaleValue(String key, Object v){
        if(v instanceof String){ return SqlUtils.escapeSql((String)v);}
        return v;
    }

    private void handleQueryWrapper(String k, Object v, QueryWrapper<T> w, Map<Integer, Map<String,String>> orderByMap) throws UnsupportedEncodingException {
        String key = null;
        if(k.indexOf(" ")>0){throw new RRException("非法访问!!!");}
        if(k.indexOf("(")>0){ throw new RRException("非法访问!!!");}
        if(k.indexOf(")")>0){ throw new RRException("非法访问!!!");}
        if(k.indexOf("=")>0){ throw new RRException("非法访问!!!");}
        if(k.indexOf(">")>0){ throw new RRException("非法访问!!!");}
        if(k.indexOf("<")>0){ throw new RRException("非法访问!!!");}
        if(k.indexOf("!")>0){ throw new RRException("非法访问!!!");}
        if(k.indexOf("%")>0){ throw new RRException("非法访问!!!");}
        if(k.indexOf("'")>0){ throw new RRException("非法访问!!!");}
        if (v instanceof String) {
            v = URLDecoder.decode(URLEncoder.encode(v.toString(), String.valueOf(StandardCharsets.UTF_8)), String.valueOf(StandardCharsets.UTF_8));//去除空格
            if(((String) v).indexOf("<script>")>0||((String) v).indexOf("<script ")>0) throw new RRException("非法访问!!!");
        }
        Function2<String, Object, Object> parseValueFunc;
//        if("oracle".equalsIgnoreCase(Constants.DB_TYPE)){
//            parseValueFunc =this::parseDateValForOracle;
//        }else{
            parseValueFunc = this::escaleValue;
//        }
        if (k.startsWith(SYM_EQUAL)) { // = equal
            log.info("key:{},v:{}",key,v);
            w.eq(key = camelToUnderline(k, SYM_EQUAL), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_NOT_EQUAL)) { // != not equal
            w.ne(key = camelToUnderline(k, SYM_NOT_EQUAL), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_LESS_THAN)) { // < less than
            w.lt(key = camelToUnderline(k, SYM_LESS_THAN), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_LESS_OR_EQUAL_THAN)) { // <= less or equal
            w.le(key = camelToUnderline(k, SYM_LESS_OR_EQUAL_THAN), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_GREATER_THAN)) { // >  greater than
            w.gt(key = camelToUnderline(k, SYM_GREATER_THAN), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_GREATER_OR_EQUALTHAN)) { // >= greater or equal
            w.ge(key = camelToUnderline(k, SYM_GREATER_OR_EQUALTHAN), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_IN)) { // in
            w.in(key = camelToUnderline(k, SYM_IN), Arrays.stream(v.toString().split(",")).map(s->parseValueFunc.get(k, s)).collect(Collectors.toList()));
        } else if (k.startsWith(SYM_NOT_IN)) { // not In
            w.notIn(key = camelToUnderline(k, SYM_NOT_IN), Arrays.stream(v.toString().split(",")).map(s->parseValueFunc.get(k,s)).collect(Collectors.toList()));
        } else if (k.startsWith(SYM_LIKE)) { // %v% like
            w.like(key = camelToUnderline(k, SYM_LIKE), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_NOT_LIKE)) {
            w.notLike(key = camelToUnderline(k, SYM_NOT_LIKE), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_LEFT_LIKE)) { // v% left like
            w.likeLeft(key = camelToUnderline(k, SYM_LEFT_LIKE), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_RIGHT_LIKE)) { // %v right like
            w.likeRight(key = camelToUnderline(k, SYM_RIGHT_LIKE), parseValueFunc.get(key, v));
        } else if (k.startsWith(SYM_IS_NULL)) { // is null
            w.isNull(key = camelToUnderline(k, SYM_IS_NULL));
        } else if (k.startsWith(SYM_IS_NOT_NULL)) { // is not null
            w.isNotNull(key = camelToUnderline(k, SYM_IS_NOT_NULL));
        } else if (k.startsWith(SYM_GROUP_BY)) { // group by
            w.groupBy(v.toString().split(","));
        } else if (k.startsWith(SYM_HAVING)) { // having
            w.having(v.toString());
        } else if (k.startsWith(SYM_ORDER_BY_ASCENDING)) { // order by
            int vo=0;
            try{
                vo=Integer.parseInt(v.toString());
            }catch (Exception ignored){
            }
            if(!orderByMap.containsKey(vo)){orderByMap.put(vo, new HashMap<>());}
            orderByMap.get(vo).put(camelToUnderline(k, SYM_ORDER_BY_ASCENDING), k);
//			w.orderByDesc(camelToUnderline(k, SYM_ORDER_BY_ASCENDING));
        } else if (k.startsWith(SYM_ORDER_BY_DESCENDING)) { // order by
            int vo=0;
            try{
                vo=Integer.parseInt(v.toString());
            }catch (Exception ignored){
            }
            if(! orderByMap.containsKey(vo)){ orderByMap.put(vo, new HashMap<>());}
            orderByMap.get(vo).put(camelToUnderline(k, SYM_ORDER_BY_DESCENDING), k);
//			w.orderByDesc(camelToUnderline(k, SYM_ORDER_BY_DESCENDING));
        } else {
            key = k;
        }/*else if(k.equals("and")){ // and Map<String, Object> // 提供嵌套的 and, or, 有点复杂, 暂不实现

						}else if(k.equals("or")){ // or Map<String, Object>

						}*/
        if (StringUtils.isNotBlank(key)) {
            int dotPos = key.indexOf(".");
            if (dotPos >= 0){ key = key.substring(dotPos + 1);}
            unwrappedArgs.put(key, v);
        }
    }



//    SysLogEntity sysLog = new SysLogEntity();
//    SysLog syslog = method.getAnnotation(SysLog.class);
//		if(syslog != null){
//        //注解上的描述
//        sysLog.setOperation(syslog.value());
//    }
//

    public QueryWrapper<T> where() {
        if (_queryWrapper != null) return _queryWrapper;
        _queryWrapper = new QueryWrapper<>();

        Map<Integer, Map<String,String>> orderByMap= Maps.newHashMap();


//        if(where.size()>0){
//         //判断有多少种表进行关联查询
//            Set<String> ukeys = new HashSet<>();
//            where.forEach((key,val)->{
//                ukeys.add(key.split("\\.")[0].split("_")[1]);
//            });
//            ukeys.forEach(x->{
////                where.put("ne_"+x+".recordStatus", RecordStatusEnu.DELETE.getValue());
//                where.put("eq_"+x+".recordStatus", RecordStatusEnu.ENABLE.getValue());
//                where.put("eq_"+x+".officeCode", ShiroUtils.getOfficeCode());
//            });
//        }else if(StringUtils.isNotBlank(ShiroUtils.getOfficeCode())){
//            where.put("eq_"+aliasName+".recordStatus", RecordStatusEnu.ENABLE.getValue());
//            where.put("eq_"+aliasName+".officeCode", ShiroUtils.getOfficeCode());
//        }else if(StringUtils.isNotBlank(ShiroUtils.getCompanyCode())){
//            where.put("eq_"+aliasName+".recordStatus", RecordStatusEnu.ENABLE.getValue());
//            where.put("eq_"+aliasName+".companyCode", ShiroUtils.getCompanyCode());
//        }else{
//            where.put("eq_"+aliasName+".recordStatus", RecordStatusEnu.ENABLE.getValue());
//        }


        where.forEach((key, v) -> {
            boolean handle = v != null;

            if (v instanceof String) handle = StringUtils.isNotBlank(v.toString());
            if (handle && StringUtils.isNotBlank(key) && !"current".equals(key) && !"size".equals(key)) {
                List<String> keys = Lists.newArrayList(key.split("[*]"));
                if (keys.size() == 1) {
                    List<String> newKeys = Lists.newArrayList(key.split("[+]"));
                    if(newKeys.size()==1) {
                        try {
                            handleQueryWrapper(key, v, _queryWrapper, orderByMap);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }else{
                        newKeys.forEach(k-> {
                            try {
                                handleQueryWrapper(k, v, _queryWrapper, orderByMap);
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        });
                    }
                } else {
                    _queryWrapper.and(x -> {
                        int kSize = keys.size();
                        for (int i = 0; i < kSize; i++) {
                            String k = keys.get(i);
                            try {
                                handleQueryWrapper(k, v, x, orderByMap);
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                            if (i < kSize - 1) x.or();
                        }
//						return x;
                    });
                }
            }
        });
        if(CollectionUtils.isNotEmpty(orderByMap)){
            orderByMap.keySet().stream().sorted().forEach(itg->{
                Map<String, String> odMap=orderByMap.get(itg);
                for(String f: odMap.keySet()){
                    String k=odMap.get(f);
                    if(k.startsWith(SYM_ORDER_BY_ASCENDING)){
                        _queryWrapper.orderByAsc(f);
                    }else if(k.startsWith(SYM_ORDER_BY_DESCENDING)){
                        _queryWrapper.orderByDesc(f);
                    }else{
                        System.out.println("ignored unknown orderby, field="+f);
                    }
                }
            });
        }
        return _queryWrapper;
    }
    public Object getParameter(String key) {
        if (_queryWrapper == null) where();
        return unwrappedArgs.get(key);
    }

    public WhereCondition() {

    }
    public WhereCondition(QueryWrapper<T> wrapper) {
        _queryWrapper = wrapper;
    }

    public WhereCondition(HashMap<String, Object> wrapper) {
        setWhere(wrapper);
    }

}
